module(...,package.seeall)
--redefine pcall for debug trace
local function pcall(a)
	return xpcall(a,debug.traceback)
end
local function tolines(s)
	local ts = {}
	if type(s)~='string' then return ts end
	local posa = 1
	local i=1
	while 1 do
		local pos, chars = s:match('()([\r\n].?)', posa)
		if pos then
			table.insert(ts,string.format('%3d:%s',i,s:sub(posa, pos-1)))
			if chars == '\r\n' then posa = pos + 2 else posa = pos + 1 end
			i=i+1
		else 
			table.insert(ts,string.format('%3d:%s',i,s:sub(posa)))
			i=i+1
			break
		end
	end
	return ts
end
local function fd_indexT(t,k)
	local v=t._T[k]
	if v~=nil then return v end
	return t.___ENV[k]
end
local function fd_newindexT(t,k,v)
	t._T[k]=v
end
local fd_meta={__index = fd_indexT,__newindex=fd_newindexT}

--quickly do code in env, and return ok+'all return values' or nil+errmsg
function fast_do(env,code,...)
	local f, err = code
	if type(code) ~= 'function' then
		f,err = loadstring(code)
	end
	if not f then return f,err end
	setfenv(f,env or {})
	return pcall(f,...)
end

function convval(val)
	local _,r=assert(fast_do(nil,'return '..val))
	return r
end
function autostr2table(val)
	if type(val)=='string' and string.byte(val,1)==0x7b then
		return convval(val)
	else
		return val
	end
end
--fast_do return the 'new value table' and 'one return value' or nil+errtbl
function conf_do(lua_code,env,logger)
	local f, err
	if type(lua_code) ~= 'function' then
		f,err = loadstring(lua_code)
	else
		f=lua_code
	end
	local gtr,r_value
	if f then 
		local gt={_T={}}
		if env then
			gt.___ENV=env
			gt._T._T=gt._T
			setmetatable(gt, fd_meta)
		else
			gt._T=gt
		end
		setfenv(f, gt)	-- set a restricted environment
		local ok, result = pcall(f)
		gtr=gt._T
		gtr._T=nil
		if ok then 
			r_value = result 
		else
			err = result
		end
	end

	if err then
		if logger then
			local inft={
				"sandbox: couldn't execute",
				"Error msg: "..err,
				"Error on: "..string.gsub(err, "^.+%]%:(%d+)%:", "On line %1:"),
			}
			if type(lua_code)=='string' then
				table.insert(inft,"Source code: \n"..table.concat(tolines(lua_code),'\n'))
			end
			if env then
				table.insert(inft,"environment:")
				for k,v in pairs(env) do
					table.insert(inft,string.format("%s=%q", tostring(k), tostring(v)))
				end
			end
			logger:error(table.concat(inft,'\n'))
		end
		return nil, err
	else
		return gtr,r_value
	end
end

function conf_do_many(env,logger,...)
	local fnum=select("#",...)
	if fnum>0 then
		local gtr,r_value
		local f, err
		local gt={_T={}}
		if env then
			gt.___ENV=env
			gt._T._T=gt._T
			setmetatable(gt, fd_meta)
		else
			gt._T=gt
		end
		for i = 1, select("#",...) do
			local lua_code=select(i,...)
			local f, err = lua_code
			if type(f) ~= 'function' then
				f,err = loadstring(f)
			end
			if f then
				setfenv(f, gt)	-- set a restricted environment
				f,err= pcall(f)
			end
			if not f then
				if logger then
					local inft={
						"odc.luaenv: couldn't execute lua",
						"Error on: "..string.gsub(err, "^.+%]%:(%d+)%:", "On line %1:"),
						"Source code: \n"..table.concat(tolines(lua_code),'\n'),
						"environment:"
					}
					if env then
						for k,v in pairs(env) do
							table.insert(inft,string.format("%s=%q", tostring(k), tostring(v)))
						end
					end
					table.insert(inft,err)
					logger:error(table.concat(inft,'\n'))
				end
				return nil, err
			end
		end

		gtr=gt._T
		gtr._T=nil
		return gtr,r_value
	else
		return nil,'no code to run'
	end
end